package mqtt

import (
	"encoding/json"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"gorm.io/gorm/clause"
	global2 "longmen/server/config/global"
	"longmen/server/internal/services/nami/services/sports/basketball"
	bbcache2 "longmen/server/internal/services/nami/services/sports/basketball/bbcache"
	utils2 "longmen/server/internal/services/nami/utils"
	"longmen/server/pkg/db/mysql/models/models_sports"
	"strconv"
	"strings"
)

const basketBallTopicName = "sports/basketball/match.v5"

// 比分字段说明(score)
//[
//	3452907,//比赛id
//	10,//比赛状态，详见状态码->比赛状态
//	0, //小节剩余时间(秒)
//	[//主队比分
//		23,//第1节分数
//		32,//第2节分数
//		22,//第3节分数
//		34,//第4节分数
//		0//加时分数
//	],
//	[//客队比分
//		28,
//		23,
//		21,
//		26,
//		0
//	]
//]

type (
	BasketBallMQ       struct{}
	BasketBallScores   []interface{}
	BasketBallTextLive [][]string
	BasketBallStats    [][]float64
	BasketPlayers      []interface{}
	BasketBallResponse struct {
		ID      int                `json:"id"`
		Score   BasketBallScores   `json:"score"`
		Stats   BasketBallStats    `json:"stats"`
		Tlive   BasketBallTextLive `json:"tlive"`
		Players BasketPlayers      `json:"players"`
	}
)

// GetStats 获取统计数据
// [
//
//	[
//		1,//统计类型，请参考 状态码->篮球技术统计
//		8,//主队数值
//		10//客队数值
//	],
//	[
//		4,//暂停数，当前小节剩余暂停数，次节重置，需自行记录
//		2,
//		1
//	],
//	[
//		5,//犯规数，当前小节犯规数，次节重置，需自行记录
//		3,
//		5
//	]
//
// ]
const (
	BBThreePointShot = 1 // 三分球
	BBTwoPointShot   = 2 // 两分球
	BBPauseType      = 7 // 暂停计数统计
)

const BBTotalPause = 5 // 篮球总暂停数

func (b BasketBallStats) GetStats(matchID int) []bbcache2.Stats {
	records := make([]bbcache2.Stats, 0)
	for _, val := range b {
		tt := int(val[0])
		home := val[1]
		away := val[2]
		switch tt {
		// 三分球 转换
		case BBThreePointShot:
			home *= 3
			away *= 3
		// 两分球转换
		case BBTwoPointShot:
			home *= 2
			away *= 2
		// 将暂停总数转换成暂停剩余数量
		case BBPauseType:
			home = BBTotalPause - home
			away = BBTotalPause - away
		}

		records = append(records, bbcache2.Stats{
			MatchId: matchID,
			Type:    tt,
			Home:    home,
			Away:    away,
		})
	}
	return records
}

//tlive字段为列表，表示不同节数的数据
//[
//	[//第一小节
//	"1^12:00^1^0^0-0^特里斯坦·汤普森 vs. 尼古拉·武切维奇 (乔纳森·艾萨克 得到球)^11362,11358,12353^0^0^0^0,0",

//	"5^11:45^1^0^0-0^阿隆·戈登 两分投篮不中^12051^1^0^0^25,8",//(兼容忽略)^时间^主客队（0-中立，1-主队，2-客队）^0^客队比分-主队比分^说明^(兼容忽略)

//	 5^ 11:45 ^ 主/客ID ^ 0 ^ 主分 ^ 客分 ^ 文字
//	],
//	[//后续小节，同第一小节
//	]
//
// ]
func (b BasketBallTextLive) GetTextLIve() []bbcache2.TextLive {
	records := make([]bbcache2.TextLive, 0)
	for _, i2 := range b {
		for _, s := range i2 {
			split := strings.Split(s, "^")
			score := strings.Split(split[4], "-")
			team, _ := strconv.Atoi(split[2])
			awayScore, _ := strconv.Atoi(score[0])
			homeScore, _ := strconv.Atoi(score[1])
			records = append(records, bbcache2.TextLive{
				Time:      split[1],
				Position:  team,
				AwayScore: awayScore,
				HomeScore: homeScore,
				Data:      split[5],
			})
		}
	}
	return records
}
func (b BasketBallScores) GetScore() bbcache2.Score {
	if len(b) < 5 {
		return bbcache2.Score{}
	}
	homeScore := utils2.SliceInterfaceToIntSlice(b[3].([]interface{}))
	awayScore := utils2.SliceInterfaceToIntSlice(b[4].([]interface{}))
	homeTotalScore := utils2.SumInt(homeScore)
	awayTotalScore := utils2.SumInt(awayScore)
	return bbcache2.Score{
		MatchId:        int(b[0].(float64)), // 外部再填充
		MatchStatus:    int(b[1].(float64)),
		TimeLeft:       int(b[2].(float64)),
		AwayScore:      awayScore,
		HomeScore:      homeScore,
		HomeTotalScore: homeTotalScore,
		AwayTotalScore: awayTotalScore,
	}
}
func (b BasketBallScores) GetUpdate() models_sports.BasketballMatch {
	if len(b) < 5 {
		return models_sports.BasketballMatch{}
	}
	homeScores, _ := json.Marshal(b[3])
	awayScores, _ := json.Marshal(b[4])
	status := models_sports.BasketBallMatchStatus(b[1].(float64))
	return models_sports.BasketballMatch{
		ID:         int(b[0].(float64)),
		StatusID:   status,
		HomeScores: string(homeScores),
		AwayScores: string(awayScores),
		TimePlayed: status.GetTimePlayed(int(b[2].(float64))),
	}
}

// GetPlayer [
//
//	[//主队
//		[
//			1126,//球员id
//			"J.J. 巴里亚",//球员中文名称
//			"J.J. 巴里亚",//球员粤语名称
//			"J.J. Barea",//球员英文名称
//			"https://cdn.sportnanoapi.com/basketball/player/143a4b49049f75c3f7ce5e5275c0d26f.png",//球员logo
//			"5",//球衣号
//			"27^1-9^1-5^0-0^0^7^7^4^0^0^1^2^+2^3^1^0^1"//在场持续时间^命中次数-投篮次数^三分球投篮命中次数-三分投篮次数^罚球命中次数-罚球投篮次数^进攻篮板^防守篮板^总的篮板^助攻数^抢断数^盖帽数^失误次数^个人犯规次数^+/-值^得分^是否出场(1-出场，0-没出场)^是否在场上（0-在场上，1-没在场上）（用于赛中）^是否是替补（1-替补，0-首发）
//		],
//	]
//	[ //客队，同主队 ],
//	"0^37-86^9-30^11-19^7^39^46^24^12^4^13^18^0^94",//球队数据，主队
//	"0^33-89^9-34^10-15^10^44^54^20^8^2^16^15^0^85"//球队数据，客队   (兼容忽略)^命中次数-投篮次数^三分球投篮命中次数-三分投篮次数^罚球命中次数-罚球投篮次数^进攻篮板^防守篮板^总的篮板^助攻数^抢断数^盖帽数^失误次数^个人犯规次数^(兼容忽略)^得分
//
// ]
func (p BasketPlayers) GetPlayer(isHome int) []bbcache2.Player {
	records := make([]bbcache2.Player, 0)

	if len(p) < 2 {
		return records
	}

	players := p[isHome].([]interface{})
	getString := func(i interface{}) string {
		s, ok := i.(string)
		if !ok {
			return ""
		}
		return s
	}
	for _, player := range players {
		temp, ok := player.([]interface{})
		if !ok || len(temp) < 7 {
			continue
		}
		playerId, ok := temp[0].(float64)
		if !ok {
			playerId = 0
		}
		records = append(records, bbcache2.Player{
			Id:       int64(playerId),
			NameZh:   getString(temp[1]),
			NameZht:  getString(temp[2]),
			NameEn:   getString(temp[3]),
			Logo:     getString(temp[4]),
			Number:   getString(temp[5]),
			TimeLine: p.GetTimeLine(getString(temp[6])),
		})
	}
	return records
}
func (p BasketPlayers) GetTimeLine(timeLine string) bbcache2.TimeLineItem {
	split := strings.Split(timeLine, "^")
	getInt := func(s string) int {
		intres, _ := strconv.Atoi(s)
		return intres
	}
	if len(split) < 12 {
		return bbcache2.TimeLineItem{}
	}
	hit := strings.Split(split[1], "-")
	goal := strings.Split(split[2], "-")
	fq := strings.Split(split[3], "-")

	data := bbcache2.TimeLineItem{
		PlayingTime:       getInt(split[0]),
		HitCount:          getInt(hit[0]),
		ShotCount:         getInt(hit[1]),
		Goal3Score:        getInt(goal[0]),
		Goal3ScoreCount:   getInt(goal[1]),
		FQHitCount:        getInt(fq[0]),
		FQCount:           getInt(fq[1]),
		OffensiveRebounds: getInt(split[4]),
		DefensiveRebounds: getInt(split[5]),
		TotalRebounds:     getInt(split[6]),
		Assists:           getInt(split[7]),
		Steals:            getInt(split[8]),
		Caps:              getInt(split[9]),
		MistakeCount:      getInt(split[10]),
		Fouls:             getInt(split[11]),
		TotalScore:        0,
	}
	// 三分球 + 罚球命中次数 + （命中次数 - 3分球） *2
	data.TotalScore = data.Goal3Score*3 + data.FQHitCount*1 + (data.HitCount-data.Goal3Score)*2
	return data
}
func (p BasketPlayers) GetLineupCustom(matchId int) []models_sports.BasketBallLineupCustom {
	custom := p.lineupCustom(matchId, 1)
	custom = append(custom, p.lineupCustom(matchId, 0)...)
	return custom
}

func (p BasketPlayers) lineupCustom(matchId, isHome int) []models_sports.BasketBallLineupCustom {
	records := make([]models_sports.BasketBallLineupCustom, 0)
	if len(p) < 2 {
		return records
	}
	players := p[isHome].([]interface{})
	getString := func(i interface{}) string {
		s, ok := i.(string)
		if !ok {
			return ""
		}
		return s
	}
	for _, player := range players {
		temp, ok := player.([]interface{})
		if !ok || len(temp) < 7 {
			continue
		}
		playerId, ok := temp[0].(float64)
		if !ok {
			playerId = 0
		}

		records = append(records, models_sports.BasketBallLineupCustom{
			MatchId:  matchId,
			TeamType: isHome,
			PlayerId: int64(playerId),
			NameZh:   getString(temp[1]),
			NameZht:  getString(temp[2]),
			NameEn:   getString(temp[3]),
			Logo:     getString(temp[4]),
			Number:   getString(temp[5]),
			TimeLine: getString(temp[6]),
		})
	}
	return records
}

func (p BasketPlayers) GetPlayers(matchId int) bbcache2.LineUp {
	//marshal, _ := json.Marshal(p)
	//global.Log.Info("player: ", string(marshal))
	getString := func(i interface{}) string {
		s, ok := i.(string)
		if !ok {
			return ""
		}
		return s
	}
	return bbcache2.LineUp{
		MatchId:      matchId,
		Home:         p.GetPlayer(0),
		Away:         p.GetPlayer(1),
		HomeTimeLine: p.GetTimeLine(getString(p[2])),
		AwayTimeLine: p.GetTimeLine(getString(p[3])),
	}
}

func (f *BasketBallMQ) InitTopic(client mqtt.Client) {
	token := client.Subscribe(basketBallTopicName, 1, f.MessageHandler)
	token.Wait()
	global2.Log.Info("init topic 篮球: ", basketBallTopicName)
}

func (f *BasketBallMQ) MessageHandler(client mqtt.Client, msg mqtt.Message) {
	err := f.Exec(msg.Payload(), msg.MessageID())
	if err != nil {
		msg.Retained()
		return
	}
	msg.Ack()
}

func (f *BasketBallMQ) Exec(body []byte, MessageID uint16) error {
	global2.Log.Info("basketball receive: ", string(body))
	records := make([]BasketBallResponse, 0)
	err := json.Unmarshal(body, &records)
	if err != nil {
		global2.Log.Error("反序列化篮球数据失败：", err, string(body))
		return err
	}

	err = f.publishLiveScores(records)
	if err != nil {
		return err
	}

	err = f.publishTextLive(records)
	if err != nil {
		return err
	}

	err = f.publishStatScores(records)
	if err != nil {
		return err
	}

	err = f.publishPlayers(records)
	if err != nil {
		return err
	}
	return nil
}

// publishPlayers 实时阵容推送
func (f *BasketBallMQ) publishPlayers(records []BasketBallResponse) error {
	playersDb := make([]models_sports.BasketBallLineupCustom, 0)
	for _, record := range records {
		if len(record.Players) == 0 {
			continue
		}
		global2.Log.Debugf("接收到篮球阵容数据,mid:%d,data:%v", record.ID, record.Players)
		err := bbcache2.NewLineUpCache().PublishPlayers(record.Players.GetPlayers(record.ID))
		if err != nil {
			global2.Log.Error("publish lineup  error: ", err)
		}

		playersDb = append(playersDb, record.Players.GetLineupCustom(record.ID)...)
	}

	if len(playersDb) > 0 {
		clauses := clause.OnConflict{
			UpdateAll: true,
		}
		err := global2.Sport().Clauses(clauses).CreateInBatches(playersDb, 200).Error
		if err != nil {
			return err
		}
	}

	return nil
}

// publishStatScores 实时技术统计
func (f *BasketBallMQ) publishStatScores(records []BasketBallResponse) error {
	res := make([]bbcache2.Stats, 0)
	for _, record := range records {
		res = append(res, record.Stats.GetStats(record.ID)...)
	}
	if len(res) > 0 {
		err := bbcache2.NewStatsCache().PublishStats(res)
		if err != nil {
			return err
		}
	}

	return nil
}

// publishLiveScores 实时比分更新
func (f *BasketBallMQ) publishLiveScores(records []BasketBallResponse) error {
	updateInterfaces := make([]basketball.UpdateMatchInterface, 0)
	sciface := make([]bbcache2.PublishScoreInterface, 0)
	for _, record := range records {
		updateInterfaces = append(updateInterfaces, record.Score)
		sciface = append(sciface, record.Score)
	}

	if len(updateInterfaces) > 0 {

		err := bbcache2.NewScoreCache().PublishLiveScore(sciface)
		if err != nil {
			global2.Log.Error("models_live score error: ", err)
		}
		basketball.UpdateMatchScore(updateInterfaces)
	}
	return nil
}

// publishTextLive 文字直播数据推送
func (f *BasketBallMQ) publishTextLive(resp []BasketBallResponse) error {
	records := make([]bbcache2.TextLive, 0)
	for _, item := range resp {
		if len(item.Tlive) == 0 {
			continue
		}
		// 多个比赛都揉到一起推送
		temp := item.Tlive.GetTextLIve()
		for i := range temp {
			temp[i].MatchId = item.ID
		}
		records = append(records, temp...)
	}
	if len(records) > 0 {
		err := bbcache2.NewTextLiveCache().PublishTextLive(records)
		if err != nil {
			global2.Log.Error("发布订阅数据失败：", err)
			return err
		}
	}
	return nil
}

func (f *BasketBallMQ) Quit(client mqtt.Client) {
	client.Unsubscribe(basketBallTopicName)
}
